/****************************************************************************
 * Copyright (c) 2006, 2008 Remy Suen, Composent Inc., and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *    Remy Suen <remy.suen@gmail.com> - initial API and implementation
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package org.eclipse.ecf.internal.provider.bittorrent;

import java.io.IOException;
import java.util.Date;

import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.filetransfer.IFileRangeSpecification;
import org.eclipse.ecf.filetransfer.IFileTransferListener;
import org.eclipse.ecf.filetransfer.IFileTransferPausable;
import org.eclipse.ecf.filetransfer.IFileTransferRateControl;
import org.eclipse.ecf.filetransfer.IIncomingFileTransfer;
import org.eclipse.ecf.filetransfer.IOutgoingFileTransfer;
import org.eclipse.ecf.filetransfer.UserCancelledException;
import org.eclipse.ecf.filetransfer.events.IIncomingFileTransferReceiveDataEvent;
import org.eclipse.ecf.filetransfer.events.IIncomingFileTransferReceiveDoneEvent;
import org.eclipse.ecf.protocol.bittorrent.IPieceProgressListener;
import org.eclipse.ecf.protocol.bittorrent.ITorrentStateListener;
import org.eclipse.ecf.protocol.bittorrent.Torrent;

final class TorrentFileTransfer implements IFileTransferPausable, IFileTransferRateControl, IIncomingFileTransfer, IOutgoingFileTransfer {

	private final ID id;

	private final IFileTransferListener listener;

	private final Torrent torrent;

	private final IPieceProgressListener pieceListener;

	private final ITorrentStateListener stateListener;

	private final double total;

	private Exception exception;

	private boolean paused = false;

	TorrentFileTransfer(ID id, IFileTransferListener listener, Torrent torrent) {
		this.id = id;
		this.listener = listener;
		this.torrent = torrent;
		total = torrent.getTorrentFile().getTotalLength();

		pieceListener = new IPieceProgressListener() {
			public void blockDownloaded(int piece, int index, int blockLength) {
				TorrentFileTransfer.this.listener.handleTransferEvent(new IIncomingFileTransferReceiveDataEvent() {

					public IIncomingFileTransfer getSource() {
						return TorrentFileTransfer.this;
					}

				});
			}
		};

		stateListener = new ITorrentStateListener() {
			public void stateChanged(int state) {
				if (state == ITorrentStateListener.FINISHED) {
					notifyCompletion(null);
				}
			}
		};

		torrent.addPieceProgressListener(pieceListener);
		torrent.addTorrentStateListener(stateListener);

		try {
			torrent.start();
		} catch (final IOException e) {
			notifyCompletion(e);
		}
	}

	private void notifyCompletion(Exception exception) {
		this.exception = exception;
		torrent.removePieceProgressListener(pieceListener);
		torrent.removeTorrentStateListener(stateListener);

		listener.handleTransferEvent(new IIncomingFileTransferReceiveDoneEvent() {

			public Exception getException() {
				return TorrentFileTransfer.this.exception;
			}

			public IIncomingFileTransfer getSource() {
				return TorrentFileTransfer.this;
			}

		});
	}

	public void cancel() {
		try {
			torrent.stop();
			notifyCompletion(new UserCancelledException());
		} catch (final IOException e) {
			notifyCompletion(e);
		}
	}

	public long getBytesSent() {
		return torrent.getUploaded();
	}

	public Exception getException() {
		return exception;
	}

	public double getPercentComplete() {
		return (total - torrent.getRemaining()) / total;
	}

	public Object getAdapter(Class adapter) {
		return adapter.isInstance(this) ? this : null;
	}

	public ID getID() {
		return id;
	}

	public long getBytesReceived() {
		return torrent.getDownloaded();
	}

	public boolean isDone() {
		return exception != null || getPercentComplete() == 1;
	}

	public boolean pause() {
		if (paused || isDone()) {
			return false;
		}

		try {
			torrent.stop();
			paused = true;
			return true;
		} catch (final IOException e) {
			notifyCompletion(e);
			return false;
		}
	}

	public boolean resume() {
		if (!paused || isDone()) {
			return false;
		}

		try {
			torrent.start();
			paused = false;
			return true;
		} catch (final IOException e) {
			notifyCompletion(e);
			return false;
		}
	}

	public void setMaxDownloadSpeed(long maxDownloadSpeed) {
		torrent.setMaxDownloadSpeed(maxDownloadSpeed);
	}

	public void setMaxUploadSpeed(long maxUploadSpeed) {
		torrent.setMaxUploadSpeed(maxUploadSpeed);
	}

	public boolean isPaused() {
		return paused;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ecf.filetransfer.IIncomingFileTransfer#getListener()
	 */
	public IFileTransferListener getListener() {
		return listener;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ecf.filetransfer.IIncomingFileTransfer#getFileRangeSpecification()
	 */
	public IFileRangeSpecification getFileRangeSpecification() {
		return null;
	}

	public long getFileLength() {
		return torrent.getTorrentFile().getTotalLength();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ecf.filetransfer.IIncomingFileTransfer#getRemoteFileName()
	 */
	public String getRemoteFileName() {
		if (torrent == null)
			return null;
		if (!torrent.getTorrentFile().isMultiFile()) {
			return torrent.getTorrentFile().getFilenames()[0];
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ecf.filetransfer.IIncomingFileTransfer#getRemoteLastModified()
	 */
	public Date getRemoteLastModified() {
		// Not supported
		return null;
	}

}
